#include "clientFunc.h"

#define SHA1LEN 40
#define BUFSIZE 8192

// 根据传入的路径(cmd_t结构体中的一个argv字符串,可相对可绝对)
// 获取真实的Linux绝对路径存入realPath中
int realPath(const char *argv, char *realPath)
{
    if(argv[0] != '/'){
        // 相对路径
        char cwd[1024] = {0};
        getcwd(cwd,sizeof(cwd));
        sprintf(realPath,"%s%s%s",cwd,"/",argv);
    }
    else {
        // 绝对路径
        sprintf(realPath,"%s",argv);
    }
    return 0;
}

// 判断路径是否和传入类型匹配
// 传入S_IFREG(普通文件)或S_IFDIR(目录)判断类型
// 返回1类型相符, 返回0类型不符, 返回-1路径无法访问
int pathJudge(const char* realPath, mode_t mode) {
    if(access(realPath, F_OK) == 0) {
        struct stat fileInfo;
        ssize_t sret = stat(realPath, &fileInfo);
        ERROR_CHECK(sret, -1, "stat");
        if((fileInfo.st_mode & S_IFMT) == mode) {
            return 1;
        }
        else {
            return 0;
        }
    }
    return -1;
}

// 计算sha1sum,结果为长度为40的十六进制数字符串
// 需要打印字符串时最好创建大小为41的数组
static int sha1Sum(const char *fileName,char *sha1Num)
{
    // 打开文件
    int filefd = open(fileName,O_RDWR);
    ERROR_CHECK(filefd,-1,"open in sha1Sum");
    // 初始化
    EVP_MD_CTX *mdctx = EVP_MD_CTX_new();
    const EVP_MD *md_sha1 = EVP_sha1();
    unsigned char hash[EVP_MAX_MD_SIZE];
    unsigned int hash_len;
    EVP_DigestInit(mdctx, md_sha1);
    size_t sret;
    unsigned char buf[BUFSIZE];
    // 计算sha1
    while ((sret = read(filefd,buf,sizeof(buf))) != 0) {
        EVP_DigestUpdate(mdctx, buf, sret);
    }
    // 储存最终结果
    EVP_DigestFinal(mdctx, hash, &hash_len);
    // 二进制转换为字符,每字节转换为两个十六进制数
    for (unsigned int i = 0; i < hash_len; i++) {
        sprintf(&sha1Num[2*i],"%02x", hash[i]);
    }
    close(filefd);
    return 0;
}

// 上传文件clientPuts
// sha1Send函数,计算sha1sum并发送
int sha1Send(int sockfd, const char *fileName)
{
    // 计算sha1sum
    char sha1Num[SHA1LEN];
    sha1Sum(fileName,sha1Num);
    // 发送
    send(sockfd,sha1Num,SHA1LEN,MSG_NOSIGNAL);
    return 0;
}

// 发送成功信息
int successSend(int sockfd) {
    send(sockfd, "o", 1, MSG_NOSIGNAL);
    return 0;
}

// 发送错误信息, 如果填入NULL, 发送一个'e'报错信号
// 如果填入字符串, 将其作为报错信息发送给客户端
int errorSend(int sockfd, const char* errorMsg) {
    if(errorMsg == NULL) {
        send(sockfd, "e", 1, MSG_NOSIGNAL);
    }
    else {
        train_t train;
        strSend(sockfd, &train, errorMsg);
    }
    return 0;
}

// 用小火车发送字符串
int strSend(int sockfd, train_t* ptrain, const char* str) {
    memset(ptrain, 0, sizeof(train_t));
    ptrain->length = strlen(str) + 1;
    memcpy(ptrain->data, str, ptrain->length);
    send(sockfd, ptrain, ptrain->length + sizeof(int), MSG_NOSIGNAL);
    return 0;
}

// 接收确认信息, 信息正确时返回1, 错误时返回0
int recvMsg(int sockfd, train_t* ptrain) {
    char msgFlag[1] = {0};
    recvn(sockfd, msgFlag, sizeof(msgFlag));
    if(msgFlag[0] == 'e') {
        memset(ptrain, 0, sizeof(train_t));
        recvn(sockfd, &ptrain->length, sizeof(int));
        recvn(sockfd, ptrain->data, ptrain->length);
        printf("%s\n", ptrain->data);
        return 0;
    }
    else if(msgFlag[0] == 'o') {
        return 1;
    }
    else if(msgFlag[0] == '\0') {
        printf("服务器已离线\n");
        userLogoutProc();
        exit(0);
    }
    return 0;
}

// 辅助函数
// recvn
int recvn(int sockfd, void *buf, int length)
{
    char *p = (char *)buf;
    while(length > 0){
        ssize_t sret = recv(sockfd,p,length,0);
        ERROR_CHECK(sret,-1,"recv in recvn");
        if(sret == 0){
            return -1;
        }
        p += sret;
        length -= sret;
    }
    return 0;
}

